"resources/src/mediawiki.action",
"resources/src/mediawiki.language",
"resources/src/mediawiki.messagePoster",
- "resources/src/mediawiki.page",
"resources/src/mediawiki.special",
"resources/src/mediawiki.toolbar",
"resources/src/mediawiki.widgets",
/* MediaWiki Page */
'mediawiki.page.gallery' => array(
- 'scripts' => 'resources/src/mediawiki.page/mediawiki.page.gallery.js',
+ 'scripts' => 'resources/src/mediawiki/page/gallery.js',
'dependencies' => array(
'mediawiki.page.gallery.styles',
'jquery.throttle-debounce',
),
'mediawiki.page.gallery.styles' => array(
'styles' => array(
- 'resources/src/mediawiki.page/mediawiki.page.gallery.print.css' => array( 'media' => 'print' ),
- 'resources/src/mediawiki.page/mediawiki.page.gallery.css',
+ 'resources/src/mediawiki/page/gallery-print.css' => array( 'media' => 'print' ),
+ 'resources/src/mediawiki/page/gallery.css',
),
'position' => 'top',
'targets' => array( 'desktop', 'mobile' ),
),
'mediawiki.page.ready' => array(
- 'scripts' => 'resources/src/mediawiki.page/mediawiki.page.ready.js',
+ 'scripts' => 'resources/src/mediawiki/page/ready.js',
'dependencies' => array(
'jquery.accessKeyLabel',
'jquery.checkboxShiftClick',
'targets' => array( 'desktop', 'mobile' ),
),
'mediawiki.page.startup' => array(
- 'scripts' => 'resources/src/mediawiki.page/mediawiki.page.startup.js',
+ 'scripts' => 'resources/src/mediawiki/page/startup.js',
'dependencies' => 'mediawiki.util',
'position' => 'top',
'targets' => array( 'desktop', 'mobile' ),
),
'mediawiki.page.patrol.ajax' => array(
- 'scripts' => 'resources/src/mediawiki.page/mediawiki.page.patrol.ajax.js',
+ 'scripts' => 'resources/src/mediawiki/page/patrol.js',
'dependencies' => array(
'mediawiki.page.startup',
'mediawiki.api',
),
),
'mediawiki.page.watch.ajax' => array(
- 'scripts' => 'resources/src/mediawiki.page/mediawiki.page.watch.ajax.js',
+ 'scripts' => 'resources/src/mediawiki/page/watch.js',
'dependencies' => array(
'mediawiki.api.watch',
'mediawiki.notify',
),
),
'mediawiki.page.image.pagination' => array(
- 'scripts' => 'resources/src/mediawiki.page/mediawiki.page.image.pagination.js',
+ 'scripts' => 'resources/src/mediawiki/page/image-pagination.js',
'dependencies' => array(
'mediawiki.Uri',
'mediawiki.util',
'position' => 'top',
'styles' => array(
// @todo: Remove mediawiki.page.gallery when cache has cleared
- 'resources/src/mediawiki.page/mediawiki.page.gallery.print.css' => array( 'media' => 'print' ),
+ 'resources/src/mediawiki/page/gallery-print.css' => array( 'media' => 'print' ),
// @todo: Remove mediawiki.action.view.filepage.print.css when cache has cleared
'resources/src/mediawiki.action/mediawiki.action.view.filepage.print.css' =>
array( 'media' => 'print' ),
'position' => 'top',
'styles' => array(
// @todo: Remove when mediawiki.page.gallery in cached html.
- 'resources/src/mediawiki.page/mediawiki.page.gallery.css',
+ 'resources/src/mediawiki/page/gallery.css',
// @todo: Remove mediawiki.action.view.filepage.css
// and mediawiki.legacy/images/checker.png when cache has cleared
'resources/src/mediawiki.action/mediawiki.action.view.filepage.css',
+++ /dev/null
-/* Galleries */
-/* These display attributes look nonsensical, but are needed to support IE and FF2 */
-/* Don't forget to update mediawiki.page.gallery.print.css */
-li.gallerybox {
- vertical-align: top;
- display: -moz-inline-box;
- display: inline-block;
-}
-
-ul.gallery,
-li.gallerybox {
- zoom: 1;
- *display: inline;
-}
-
-ul.gallery {
- margin: 2px;
- padding: 2px;
- display: block;
-}
-
-li.gallerycaption {
- font-weight: bold;
- text-align: center;
- display: block;
- word-wrap: break-word;
-}
-
-li.gallerybox div.thumb {
- text-align: center;
- border: 1px solid #ccc;
- background-color: #f9f9f9;
- margin: 2px;
-}
-
-li.gallerybox div.thumb img {
- display: block;
- margin: 0 auto;
-}
-
-div.gallerytext {
- overflow: hidden;
- font-size: 94%;
- padding: 2px 4px;
- word-wrap: break-word;
-}
-
-/* new gallery stuff */
-ul.mw-gallery-nolines li.gallerybox div.thumb {
- background-color: transparent;
- border: none;
-}
-
-ul.mw-gallery-nolines li.gallerybox div.gallerytext {
- text-align: center;
-}
-
-/* height constrained gallery */
-
-ul.mw-gallery-packed li.gallerybox div.thumb,
-ul.mw-gallery-packed-overlay li.gallerybox div.thumb,
-ul.mw-gallery-packed-hover li.gallerybox div.thumb {
- background-color: transparent;
- border: none;
-}
-
-ul.mw-gallery-packed li.gallerybox div.thumb img,
-ul.mw-gallery-packed-overlay li.gallerybox div.thumb img,
-ul.mw-gallery-packed-hover li.gallerybox div.thumb img {
- margin: 0 auto;
-}
-
-ul.mw-gallery-packed-hover li.gallerybox,
-ul.mw-gallery-packed-overlay li.gallerybox {
- position: relative;
-}
-
-ul.mw-gallery-packed-hover div.gallerytextwrapper {
- overflow: hidden;
- height: 0;
-}
-
-ul.mw-gallery-packed-hover li.gallerybox:hover div.gallerytextwrapper,
-ul.mw-gallery-packed-overlay li.gallerybox div.gallerytextwrapper,
-ul.mw-gallery-packed-hover li.gallerybox.mw-gallery-focused div.gallerytextwrapper {
- position: absolute;
- background: white;
- background: rgba(255, 255, 255, 0.8);
- padding: 5px 10px;
- bottom: 0;
- left: 0; /* Needed for IE */
- height: auto;
- font-weight: bold;
- margin: 2px; /* correspond to style on div.thumb */
-}
-
-ul.mw-gallery-packed-hover,
-ul.mw-gallery-packed-overlay,
-ul.mw-gallery-packed {
- text-align: center;
-}
+++ /dev/null
-/*!
- * Show gallery captions when focused. Copied directly from jquery.mw-jump.js.
- * Also Dynamically resize images to justify them.
- */
-( function ( mw, $ ) {
- var $galleries,
- bound = false,
- // Is there a better way to detect a touchscreen? Current check taken from stack overflow.
- isTouchScreen = !!( window.ontouchstart !== undefined ||
- window.DocumentTouch !== undefined && document instanceof window.DocumentTouch
- );
-
- /**
- * Perform the layout justification.
- *
- * @ignore
- * @context {HTMLElement} A `ul.mw-gallery-*` element
- */
- function justify() {
- var lastTop,
- $img,
- imgWidth,
- imgHeight,
- captionWidth,
- rows = [],
- $gallery = $( this );
-
- $gallery.children( 'li' ).each( function () {
- // Math.floor to be paranoid if things are off by 0.00000000001
- var top = Math.floor( $( this ).position().top ),
- $this = $( this );
-
- if ( top !== lastTop ) {
- rows[ rows.length ] = [];
- lastTop = top;
- }
-
- $img = $this.find( 'div.thumb a.image img' );
- if ( $img.length && $img[ 0 ].height ) {
- imgHeight = $img[ 0 ].height;
- imgWidth = $img[ 0 ].width;
- } else {
- // If we don't have a real image, get the containing divs width/height.
- // Note that if we do have a real image, using this method will generally
- // give the same answer, but can be different in the case of a very
- // narrow image where extra padding is added.
- imgHeight = $this.children().children( 'div:first' ).height();
- imgWidth = $this.children().children( 'div:first' ).width();
- }
-
- // Hack to make an edge case work ok
- if ( imgHeight < 30 ) {
- // Don't try and resize this item.
- imgHeight = 0;
- }
-
- captionWidth = $this.children().children( 'div.gallerytextwrapper' ).width();
- rows[ rows.length - 1 ][ rows[ rows.length - 1 ].length ] = {
- $elm: $this,
- width: $this.outerWidth(),
- imgWidth: imgWidth,
- // XXX: can divide by 0 ever happen?
- aspect: imgWidth / imgHeight,
- captionWidth: captionWidth,
- height: imgHeight
- };
-
- // Save all boundaries so we can restore them on window resize
- $this.data( 'imgWidth', imgWidth );
- $this.data( 'imgHeight', imgHeight );
- $this.data( 'width', $this.outerWidth() );
- $this.data( 'captionWidth', captionWidth );
- } );
-
- ( function () {
- var maxWidth,
- combinedAspect,
- combinedPadding,
- curRow,
- curRowHeight,
- wantedWidth,
- preferredHeight,
- newWidth,
- padding,
- $outerDiv,
- $innerDiv,
- $imageDiv,
- $imageElm,
- imageElm,
- $caption,
- i,
- j,
- avgZoom,
- totalZoom = 0;
-
- for ( i = 0; i < rows.length; i++ ) {
- maxWidth = $gallery.width();
- combinedAspect = 0;
- combinedPadding = 0;
- curRow = rows[ i ];
- curRowHeight = 0;
-
- for ( j = 0; j < curRow.length; j++ ) {
- if ( curRowHeight === 0 ) {
- if ( isFinite( curRow[ j ].height ) ) {
- // Get the height of this row, by taking the first
- // non-out of bounds height
- curRowHeight = curRow[ j ].height;
- }
- }
-
- if ( curRow[ j ].aspect === 0 || !isFinite( curRow[ j ].aspect ) ) {
- // One of the dimensions are 0. Probably should
- // not try to resize.
- combinedPadding += curRow[ j ].width;
- } else {
- combinedAspect += curRow[ j ].aspect;
- combinedPadding += curRow[ j ].width - curRow[ j ].imgWidth;
- }
- }
-
- // Add some padding for inter-element spacing.
- combinedPadding += 5 * curRow.length;
- wantedWidth = maxWidth - combinedPadding;
- preferredHeight = wantedWidth / combinedAspect;
-
- if ( preferredHeight > curRowHeight * 1.5 ) {
- // Only expand at most 1.5 times current size
- // As that's as high a resolution as we have.
- // Also on the off chance there is a bug in this
- // code, would prevent accidentally expanding to
- // be 10 billion pixels wide.
- if ( i === rows.length - 1 ) {
- // If its the last row, and we can't fit it,
- // don't make the entire row huge.
- avgZoom = ( totalZoom / ( rows.length - 1 ) ) * curRowHeight;
- if ( isFinite( avgZoom ) && avgZoom >= 1 && avgZoom <= 1.5 ) {
- preferredHeight = avgZoom;
- } else {
- // Probably a single row gallery
- preferredHeight = curRowHeight;
- }
- } else {
- preferredHeight = 1.5 * curRowHeight;
- }
- }
- if ( !isFinite( preferredHeight ) ) {
- // This *definitely* should not happen.
- // Skip this row.
- continue;
- }
- if ( preferredHeight < 5 ) {
- // Well something clearly went wrong...
- // Skip this row.
- continue;
- }
-
- if ( preferredHeight / curRowHeight > 1 ) {
- totalZoom += preferredHeight / curRowHeight;
- } else {
- // If we shrink, still consider that a zoom of 1
- totalZoom += 1;
- }
-
- for ( j = 0; j < curRow.length; j++ ) {
- newWidth = preferredHeight * curRow[ j ].aspect;
- padding = curRow[ j ].width - curRow[ j ].imgWidth;
- $outerDiv = curRow[ j ].$elm;
- $innerDiv = $outerDiv.children( 'div' ).first();
- $imageDiv = $innerDiv.children( 'div.thumb' );
- $imageElm = $imageDiv.find( 'img' ).first();
- imageElm = $imageElm.length ? $imageElm[ 0 ] : null;
- $caption = $outerDiv.find( 'div.gallerytextwrapper' );
-
- // Since we are going to re-adjust the height, the vertical
- // centering margins need to be reset.
- $imageDiv.children( 'div' ).css( 'margin', '0px auto' );
-
- if ( newWidth < 60 || !isFinite( newWidth ) ) {
- // Making something skinnier than this will mess up captions,
- if ( newWidth < 1 || !isFinite( newWidth ) ) {
- $innerDiv.height( preferredHeight );
- // Don't even try and touch the image size if it could mean
- // making it disappear.
- continue;
- }
- } else {
- $outerDiv.width( newWidth + padding );
- $innerDiv.width( newWidth + padding );
- $imageDiv.width( newWidth );
- $caption.width( curRow[ j ].captionWidth + ( newWidth - curRow[ j ].imgWidth ) );
- }
-
- if ( imageElm ) {
- // We don't always have an img, e.g. in the case of an invalid file.
- imageElm.width = newWidth;
- imageElm.height = preferredHeight;
- } else {
- // Not a file box.
- $imageDiv.height( preferredHeight );
- }
- }
- }
- }() );
- }
-
- function handleResizeStart() {
- $galleries.children( 'li' ).each( function () {
- var imgWidth = $( this ).data( 'imgWidth' ),
- imgHeight = $( this ).data( 'imgHeight' ),
- width = $( this ).data( 'width' ),
- captionWidth = $( this ).data( 'captionWidth' ),
- $innerDiv = $( this ).children( 'div' ).first(),
- $imageDiv = $innerDiv.children( 'div.thumb' ),
- $imageElm, imageElm;
-
- // Restore original sizes so we can arrange the elements as on freshly loaded page
- $( this ).width( width );
- $innerDiv.width( width );
- $imageDiv.width( imgWidth );
- $( this ).find( 'div.gallerytextwrapper' ).width( captionWidth );
-
- $imageElm = $( this ).find( 'img' ).first();
- imageElm = $imageElm.length ? $imageElm[ 0 ] : null;
- if ( imageElm ) {
- imageElm.width = imgWidth;
- imageElm.height = imgHeight;
- } else {
- $imageDiv.height( imgHeight );
- }
- } );
- }
-
- function handleResizeEnd() {
- $galleries.each( justify );
- }
-
- mw.hook( 'wikipage.content' ).add( function ( $content ) {
- if ( isTouchScreen ) {
- // Always show the caption for a touch screen.
- $content.find( 'ul.mw-gallery-packed-hover' )
- .addClass( 'mw-gallery-packed-overlay' )
- .removeClass( 'mw-gallery-packed-hover' );
- } else {
- // Note use of just "a", not a.image, since we want this to trigger if a link in
- // the caption receives focus
- $content.find( 'ul.mw-gallery-packed-hover li.gallerybox' ).on( 'focus blur', 'a', function ( e ) {
- // Confusingly jQuery leaves e.type as focusout for delegated blur events
- var gettingFocus = e.type !== 'blur' && e.type !== 'focusout';
- $( this ).closest( 'li.gallerybox' ).toggleClass( 'mw-gallery-focused', gettingFocus );
- } );
- }
-
- $galleries = $content.find( 'ul.mw-gallery-packed-overlay, ul.mw-gallery-packed-hover, ul.mw-gallery-packed' );
- // Call the justification asynchronous because live preview fires the hook with detached $content.
- setTimeout( function () {
- $galleries.each( justify );
-
- // Bind here instead of in the top scope as the callbacks use $galleries.
- if ( !bound ) {
- bound = true;
- $( window )
- .resize( $.debounce( 300, true, handleResizeStart ) )
- .resize( $.debounce( 300, handleResizeEnd ) );
- }
- } );
- } );
-}( mediaWiki, jQuery ) );
+++ /dev/null
-li.gallerybox {
- vertical-align: top;
- display: inline-block;
-}
-
-ul.gallery, li.gallerybox {
- zoom: 1;
- *display: inline;
-}
-
-ul.gallery {
- margin: 2px;
- padding: 2px;
- display: block;
-}
-
-li.gallerycaption {
- font-weight: bold;
- text-align: center;
- display: block;
- word-wrap: break-word;
-}
-
-li.gallerybox div.thumb {
- text-align: center;
- border: 1px solid #ccc;
- margin: 2px;
-}
-
-div.gallerytext {
- overflow: hidden;
- font-size: 94%;
- padding: 2px 4px;
- word-wrap: break-word;
-}
+++ /dev/null
-/*!
- * Implement AJAX navigation for multi-page images so the user may browse without a full page reload.
- */
-( function ( mw, $ ) {
- /*jshint latedef:false */
- var jqXhr, $multipageimage, $spinner,
- cache = {},
- cacheOrder = [];
-
- /* Fetch the next page, caching up to 10 last-loaded pages.
- * @param {string} url
- * @return {jQuery.Promise}
- */
- function fetchPageData( url ) {
- if ( jqXhr && jqXhr.abort ) {
- // Prevent race conditions and piling up pending requests
- jqXhr.abort();
- }
- jqXhr = undefined;
-
- // Try the cache
- if ( cache[ url ] ) {
- // Update access freshness
- cacheOrder.splice( $.inArray( url, cacheOrder ), 1 );
- cacheOrder.push( url );
- return $.Deferred().resolve( cache[ url ] ).promise();
- }
-
- // @todo Don't fetch the entire page. Ideally we'd only fetch the content portion or the data
- // (thumbnail urls) and update the interface manually.
- jqXhr = $.ajax( url ).then( function ( data ) {
- return $( data ).find( 'table.multipageimage' ).contents();
- } );
-
- // Handle cache updates
- jqXhr.done( function ( $contents ) {
- jqXhr = undefined;
-
- // Cache the newly loaded page
- cache[ url ] = $contents;
- cacheOrder.push( url );
-
- // Remove the oldest entry if we're over the limit
- if ( cacheOrder.length > 10 ) {
- delete cache[ cacheOrder[ 0 ] ];
- cacheOrder = cacheOrder.slice( 1 );
- }
- } );
-
- return jqXhr.promise();
- }
-
- /* Fetch the next page and use jQuery to swap the table.multipageimage contents.
- * @param {string} url
- * @param {boolean} [hist=false] Whether this is a load triggered by history navigation (if
- * true, this function won't push a new history state, for the browser did so already).
- */
- function switchPage( url, hist ) {
- var $tr, promise;
-
- // Start fetching data (might be cached)
- promise = fetchPageData( url );
-
- // Add a new spinner if one doesn't already exist and the data is not already ready
- if ( !$spinner && promise.state() !== 'resolved' ) {
- $tr = $multipageimage.find( 'tr' );
- $spinner = $.createSpinner( {
- size: 'large',
- type: 'block'
- } )
- // Copy the old content dimensions equal so that the current scroll position is not
- // lost between emptying the table is and receiving the new contents.
- .css( {
- height: $tr.outerHeight(),
- width: $tr.outerWidth()
- } );
-
- $multipageimage.empty().append( $spinner );
- }
-
- promise.done( function ( $contents ) {
- $spinner = undefined;
-
- // Replace table contents
- $multipageimage.empty().append( $contents.clone() );
-
- bindPageNavigation( $multipageimage );
-
- // Fire hook because the page's content has changed
- mw.hook( 'wikipage.content' ).fire( $multipageimage );
-
- // Update browser history and address bar. But not if we came here from a history
- // event, in which case the url is already updated by the browser.
- if ( history.pushState && !hist ) {
- history.pushState( { tag: 'mw-pagination' }, document.title, url );
- }
- } );
- }
-
- function bindPageNavigation( $container ) {
- $container.find( '.multipageimagenavbox' ).one( 'click', 'a', function ( e ) {
- var page, uri;
-
- // Generate the same URL on client side as the one generated in ImagePage::openShowImage.
- // We avoid using the URL in the link directly since it could have been manipulated (bug 66608)
- page = Number( mw.util.getParamValue( 'page', this.href ) );
- uri = new mw.Uri( mw.util.wikiScript() )
- .extend( { title: mw.config.get( 'wgPageName' ), page: page } )
- .toString();
-
- switchPage( uri );
- e.preventDefault();
- } );
-
- $container.find( 'form[name="pageselector"]' ).one( 'change submit', function ( e ) {
- switchPage( this.action + '?' + $( this ).serialize() );
- e.preventDefault();
- } );
- }
-
- $( function () {
- if ( mw.config.get( 'wgNamespaceNumber' ) !== 6 ) {
- return;
- }
- $multipageimage = $( 'table.multipageimage' );
- if ( !$multipageimage.length ) {
- return;
- }
-
- bindPageNavigation( $multipageimage );
-
- // Update the url using the History API (if available)
- if ( history.pushState && history.replaceState ) {
- history.replaceState( { tag: 'mw-pagination' }, '' );
- $( window ).on( 'popstate', function ( e ) {
- var state = e.originalEvent.state;
- if ( state && state.tag === 'mw-pagination' ) {
- switchPage( location.href, true );
- }
- } );
- }
- } );
-}( mediaWiki, jQuery ) );
+++ /dev/null
-/*!
- * Animate patrol links to use asynchronous API requests to
- * patrol pages, rather than navigating to a different URI.
- *
- * @since 1.21
- * @author Marius Hoch <hoo@online.de>
- */
-( function ( mw, $ ) {
- if ( !mw.user.tokens.exists( 'patrolToken' ) ) {
- // Current user has no patrol right, or an old cached version of user.tokens
- // that didn't have patrolToken yet.
- return;
- }
- $( function () {
- var $patrolLinks = $( '.patrollink a' );
- $patrolLinks.on( 'click', function ( e ) {
- var $spinner, href, rcid, apiRequest;
-
- // Start preloading the notification module (normally loaded by mw.notify())
- mw.loader.load( 'mediawiki.notification' );
-
- // Hide the link and create a spinner to show it inside the brackets.
- $spinner = $.createSpinner( {
- size: 'small',
- type: 'inline'
- } );
- $( this ).hide().after( $spinner );
-
- href = $( this ).attr( 'href' );
- rcid = mw.util.getParamValue( 'rcid', href );
- apiRequest = new mw.Api();
-
- apiRequest.postWithToken( 'patrol', {
- action: 'patrol',
- rcid: rcid
- } )
- .done( function ( data ) {
- // Remove all patrollinks from the page (including any spinners inside).
- $patrolLinks.closest( '.patrollink' ).remove();
- if ( data.patrol !== undefined ) {
- // Success
- var title = new mw.Title( data.patrol.title );
- mw.notify( mw.msg( 'markedaspatrollednotify', title.toText() ) );
- } else {
- // This should never happen as errors should trigger fail
- mw.notify( mw.msg( 'markedaspatrollederrornotify' ), { type: 'error' } );
- }
- } )
- .fail( function ( error ) {
- $spinner.remove();
- // Restore the patrol link. This allows the user to try again
- // (or open it in a new window, bypassing this ajax module).
- $patrolLinks.show();
- if ( error === 'noautopatrol' ) {
- // Can't patrol own
- mw.notify( mw.msg( 'markedaspatrollederror-noautopatrol' ), { type: 'warn' } );
- } else {
- mw.notify( mw.msg( 'markedaspatrollederrornotify' ), { type: 'error' } );
- }
- } );
-
- e.preventDefault();
- } );
- } );
-}( mediaWiki, jQuery ) );
+++ /dev/null
-( function ( mw, $ ) {
- var supportsPlaceholder = 'placeholder' in document.createElement( 'input' );
-
- // Break out of framesets
- if ( mw.config.get( 'wgBreakFrames' ) ) {
- // Note: In IE < 9 strict comparison to window is non-standard (the standard didn't exist yet)
- // it works only comparing to window.self or window.window (http://stackoverflow.com/q/4850978/319266)
- if ( window.top !== window.self ) {
- // Un-trap us from framesets
- window.top.location.href = location.href;
- }
- }
-
- mw.hook( 'wikipage.content' ).add( function ( $content ) {
- var $sortableTables;
-
- // Run jquery.placeholder polyfill if placeholder is not supported
- if ( !supportsPlaceholder ) {
- $content.find( 'input[placeholder]' ).placeholder();
- }
-
- // Run jquery.makeCollapsible
- $content.find( '.mw-collapsible' ).makeCollapsible();
-
- // Lazy load jquery.tablesorter
- $sortableTables = $content.find( 'table.sortable' );
- if ( $sortableTables.length ) {
- mw.loader.using( 'jquery.tablesorter', function () {
- $sortableTables.tablesorter();
- } );
- }
-
- // Run jquery.checkboxShiftClick
- $content.find( 'input[type="checkbox"]:not(.noshiftselect)' ).checkboxShiftClick();
- } );
-
- // Things outside the wikipage content
- $( function () {
- var $nodes;
-
- if ( !supportsPlaceholder ) {
- // Exclude content to avoid hitting it twice for the (first) wikipage content
- $( 'input[placeholder]' ).not( '#mw-content-text input' ).placeholder();
- }
-
- // Add accesskey hints to the tooltips
- if ( document.querySelectorAll ) {
- // If we're running on a browser where we can do this efficiently,
- // just find all elements that have accesskeys. We can't use jQuery's
- // polyfill for the selector since looping over all elements on page
- // load might be too slow.
- $nodes = $( document.querySelectorAll( '[accesskey]' ) );
- } else {
- // Otherwise go through some elements likely to have accesskeys rather
- // than looping over all of them. Unfortunately this will not fully
- // work for custom skins with different HTML structures. Input, label
- // and button should be rare enough that no optimizations are needed.
- $nodes = $( '#column-one a, #mw-head a, #mw-panel a, #p-logo a, input, label, button' );
- }
- $nodes.updateTooltipAccessKeys();
-
- // Infuse OOUI widgets, if any are present
- $nodes = $( '[data-ooui]' );
- if ( $nodes.length ) {
- mw.loader.using( 'mediawiki.widgets' ).done( function () {
- $nodes.each( function () {
- OO.ui.infuse( this );
- } );
- } );
- }
-
- } );
-
-}( mediaWiki, jQuery ) );
+++ /dev/null
-( function ( mw, $ ) {
-
- // Support: MediaWiki < 1.26
- // Cached HTML will not yet have this from OutputPage::getHeadScripts.
- document.documentElement.className = document.documentElement.className
- .replace( /(^|\s)client-nojs(\s|$)/, '$1client-js$2' );
-
- mw.page = {};
-
- $( function () {
- mw.util.init();
-
- /**
- * Fired when wiki content is being added to the DOM
- *
- * It is encouraged to fire it before the main DOM is changed (when $content
- * is still detatched). However, this order is not defined either way, so you
- * should only rely on $content itself.
- *
- * This includes the ready event on a page load (including post-edit loads)
- * and when content has been previewed with LivePreview.
- *
- * @event wikipage_content
- * @member mw.hook
- * @param {jQuery} $content The most appropriate element containing the content,
- * such as #mw-content-text (regular content root) or #wikiPreview (live preview
- * root)
- */
- mw.hook( 'wikipage.content' ).fire( $( '#mw-content-text' ) );
- } );
-
-}( mediaWiki, jQuery ) );
+++ /dev/null
-/**
- * Animate watch/unwatch links to use asynchronous API requests to
- * watch pages, rather than navigating to a different URI.
- *
- * @class mw.page.watch.ajax
- */
-( function ( mw, $ ) {
- // The name of the page to watch or unwatch
- var title = mw.config.get( 'wgRelevantPageName' );
-
- /**
- * Update the link text, link href attribute and (if applicable)
- * "loading" class.
- *
- * @param {jQuery} $link Anchor tag of (un)watch link
- * @param {string} action One of 'watch', 'unwatch'
- * @param {string} [state="idle"] 'idle' or 'loading'. Default is 'idle'
- */
- function updateWatchLink( $link, action, state ) {
- var msgKey, $li, otherAction;
-
- // A valid but empty jQuery object shouldn't throw a TypeError
- if ( !$link.length ) {
- return;
- }
-
- // Invalid actions shouldn't silently turn the page in an unrecoverable state
- if ( action !== 'watch' && action !== 'unwatch' ) {
- throw new Error( 'Invalid action' );
- }
-
- // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
- msgKey = state === 'loading' ? action + 'ing' : action;
- otherAction = action === 'watch' ? 'unwatch' : 'watch';
- $li = $link.closest( 'li' );
-
- // Trigger a 'watchpage' event for this List item.
- // Announce the otherAction value as the first param.
- // Used to monitor the state of watch link.
- // TODO: Revise when system wide hooks are implemented
- if ( state === undefined ) {
- $li.trigger( 'watchpage.mw', otherAction );
- }
-
- $link
- .text( mw.msg( msgKey ) )
- .attr( 'title', mw.msg( 'tooltip-ca-' + action ) )
- .updateTooltipAccessKeys()
- .attr( 'href', mw.util.wikiScript() + '?' + $.param( {
- title: title,
- action: action
- } )
- );
-
- // Most common ID style
- if ( $li.prop( 'id' ) === 'ca-' + otherAction ) {
- $li.prop( 'id', 'ca-' + action );
- }
-
- if ( state === 'loading' ) {
- $link.addClass( 'loading' );
- } else {
- $link.removeClass( 'loading' );
- }
- }
-
- /**
- * TODO: This should be moved somewhere more accessible.
- *
- * @private
- * @param {string} url
- * @return {string} The extracted action, defaults to 'view'
- */
- function mwUriGetAction( url ) {
- var action, actionPaths, key, i, m, parts;
-
- // TODO: Does MediaWiki give action path or query param
- // precedence? If the former, move this to the bottom
- action = mw.util.getParamValue( 'action', url );
- if ( action !== null ) {
- return action;
- }
-
- actionPaths = mw.config.get( 'wgActionPaths' );
- for ( key in actionPaths ) {
- if ( actionPaths.hasOwnProperty( key ) ) {
- parts = actionPaths[ key ].split( '$1' );
- for ( i = 0; i < parts.length; i++ ) {
- parts[ i ] = mw.RegExp.escape( parts[ i ] );
- }
- m = new RegExp( parts.join( '(.+)' ) ).exec( url );
- if ( m && m[ 1 ] ) {
- return key;
- }
-
- }
- }
-
- return 'view';
- }
-
- // Expose public methods
- mw.page.watch = {
- updateWatchLink: updateWatchLink
- };
-
- $( function () {
- var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
- '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
- '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' );
-
- // Allowing people to add inline animated links is a little scary
- $links = $links.filter( ':not( #bodyContent *, #content * )' );
-
- $links.click( function ( e ) {
- var action, api, $link;
-
- // Start preloading the notification module (normally loaded by mw.notify())
- mw.loader.load( 'mediawiki.notification' );
-
- action = mwUriGetAction( this.href );
-
- if ( action !== 'watch' && action !== 'unwatch' ) {
- // Could not extract target action from link url,
- // let native browsing handle it further
- return true;
- }
- e.preventDefault();
- e.stopPropagation();
-
- $link = $( this );
-
- if ( $link.hasClass( 'loading' ) ) {
- return;
- }
-
- updateWatchLink( $link, action, 'loading' );
-
- api = new mw.Api();
-
- api[ action ]( title )
- .done( function ( watchResponse ) {
- var otherAction = action === 'watch' ? 'unwatch' : 'watch';
-
- mw.notify( $.parseHTML( watchResponse.message ), {
- tag: 'watch-self'
- } );
-
- // Set link to opposite
- updateWatchLink( $link, otherAction );
-
- // Update the "Watch this page" checkbox on action=edit when the
- // page is watched or unwatched via the tab (bug 12395).
- $( '#wpWatchthis' ).prop( 'checked', watchResponse.watched !== undefined );
- } )
- .fail( function () {
- var cleanTitle, msg, link;
-
- // Reset link to non-loading mode
- updateWatchLink( $link, action );
-
- // Format error message
- cleanTitle = title.replace( /_/g, ' ' );
- link = mw.html.element(
- 'a', {
- href: mw.util.getUrl( title ),
- title: cleanTitle
- }, cleanTitle
- );
- msg = mw.message( 'watcherrortext', link );
-
- // Report to user about the error
- mw.notify( msg, {
- tag: 'watch-self',
- type: 'error'
- } );
- } );
- } );
- } );
-
-}( mediaWiki, jQuery ) );
--- /dev/null
+li.gallerybox {
+ vertical-align: top;
+ display: inline-block;
+}
+
+ul.gallery, li.gallerybox {
+ zoom: 1;
+ *display: inline;
+}
+
+ul.gallery {
+ margin: 2px;
+ padding: 2px;
+ display: block;
+}
+
+li.gallerycaption {
+ font-weight: bold;
+ text-align: center;
+ display: block;
+ word-wrap: break-word;
+}
+
+li.gallerybox div.thumb {
+ text-align: center;
+ border: 1px solid #ccc;
+ margin: 2px;
+}
+
+div.gallerytext {
+ overflow: hidden;
+ font-size: 94%;
+ padding: 2px 4px;
+ word-wrap: break-word;
+}
--- /dev/null
+/* Galleries */
+/* These display attributes look nonsensical, but are needed to support IE and FF2 */
+/* Don't forget to update mediawiki.page.gallery.print.css */
+li.gallerybox {
+ vertical-align: top;
+ display: -moz-inline-box;
+ display: inline-block;
+}
+
+ul.gallery,
+li.gallerybox {
+ zoom: 1;
+ *display: inline;
+}
+
+ul.gallery {
+ margin: 2px;
+ padding: 2px;
+ display: block;
+}
+
+li.gallerycaption {
+ font-weight: bold;
+ text-align: center;
+ display: block;
+ word-wrap: break-word;
+}
+
+li.gallerybox div.thumb {
+ text-align: center;
+ border: 1px solid #ccc;
+ background-color: #f9f9f9;
+ margin: 2px;
+}
+
+li.gallerybox div.thumb img {
+ display: block;
+ margin: 0 auto;
+}
+
+div.gallerytext {
+ overflow: hidden;
+ font-size: 94%;
+ padding: 2px 4px;
+ word-wrap: break-word;
+}
+
+/* new gallery stuff */
+ul.mw-gallery-nolines li.gallerybox div.thumb {
+ background-color: transparent;
+ border: none;
+}
+
+ul.mw-gallery-nolines li.gallerybox div.gallerytext {
+ text-align: center;
+}
+
+/* height constrained gallery */
+
+ul.mw-gallery-packed li.gallerybox div.thumb,
+ul.mw-gallery-packed-overlay li.gallerybox div.thumb,
+ul.mw-gallery-packed-hover li.gallerybox div.thumb {
+ background-color: transparent;
+ border: none;
+}
+
+ul.mw-gallery-packed li.gallerybox div.thumb img,
+ul.mw-gallery-packed-overlay li.gallerybox div.thumb img,
+ul.mw-gallery-packed-hover li.gallerybox div.thumb img {
+ margin: 0 auto;
+}
+
+ul.mw-gallery-packed-hover li.gallerybox,
+ul.mw-gallery-packed-overlay li.gallerybox {
+ position: relative;
+}
+
+ul.mw-gallery-packed-hover div.gallerytextwrapper {
+ overflow: hidden;
+ height: 0;
+}
+
+ul.mw-gallery-packed-hover li.gallerybox:hover div.gallerytextwrapper,
+ul.mw-gallery-packed-overlay li.gallerybox div.gallerytextwrapper,
+ul.mw-gallery-packed-hover li.gallerybox.mw-gallery-focused div.gallerytextwrapper {
+ position: absolute;
+ background: white;
+ background: rgba(255, 255, 255, 0.8);
+ padding: 5px 10px;
+ bottom: 0;
+ left: 0; /* Needed for IE */
+ height: auto;
+ font-weight: bold;
+ margin: 2px; /* correspond to style on div.thumb */
+}
+
+ul.mw-gallery-packed-hover,
+ul.mw-gallery-packed-overlay,
+ul.mw-gallery-packed {
+ text-align: center;
+}
--- /dev/null
+/*!
+ * Show gallery captions when focused. Copied directly from jquery.mw-jump.js.
+ * Also Dynamically resize images to justify them.
+ */
+( function ( mw, $ ) {
+ var $galleries,
+ bound = false,
+ // Is there a better way to detect a touchscreen? Current check taken from stack overflow.
+ isTouchScreen = !!( window.ontouchstart !== undefined ||
+ window.DocumentTouch !== undefined && document instanceof window.DocumentTouch
+ );
+
+ /**
+ * Perform the layout justification.
+ *
+ * @ignore
+ * @context {HTMLElement} A `ul.mw-gallery-*` element
+ */
+ function justify() {
+ var lastTop,
+ $img,
+ imgWidth,
+ imgHeight,
+ captionWidth,
+ rows = [],
+ $gallery = $( this );
+
+ $gallery.children( 'li' ).each( function () {
+ // Math.floor to be paranoid if things are off by 0.00000000001
+ var top = Math.floor( $( this ).position().top ),
+ $this = $( this );
+
+ if ( top !== lastTop ) {
+ rows[ rows.length ] = [];
+ lastTop = top;
+ }
+
+ $img = $this.find( 'div.thumb a.image img' );
+ if ( $img.length && $img[ 0 ].height ) {
+ imgHeight = $img[ 0 ].height;
+ imgWidth = $img[ 0 ].width;
+ } else {
+ // If we don't have a real image, get the containing divs width/height.
+ // Note that if we do have a real image, using this method will generally
+ // give the same answer, but can be different in the case of a very
+ // narrow image where extra padding is added.
+ imgHeight = $this.children().children( 'div:first' ).height();
+ imgWidth = $this.children().children( 'div:first' ).width();
+ }
+
+ // Hack to make an edge case work ok
+ if ( imgHeight < 30 ) {
+ // Don't try and resize this item.
+ imgHeight = 0;
+ }
+
+ captionWidth = $this.children().children( 'div.gallerytextwrapper' ).width();
+ rows[ rows.length - 1 ][ rows[ rows.length - 1 ].length ] = {
+ $elm: $this,
+ width: $this.outerWidth(),
+ imgWidth: imgWidth,
+ // XXX: can divide by 0 ever happen?
+ aspect: imgWidth / imgHeight,
+ captionWidth: captionWidth,
+ height: imgHeight
+ };
+
+ // Save all boundaries so we can restore them on window resize
+ $this.data( 'imgWidth', imgWidth );
+ $this.data( 'imgHeight', imgHeight );
+ $this.data( 'width', $this.outerWidth() );
+ $this.data( 'captionWidth', captionWidth );
+ } );
+
+ ( function () {
+ var maxWidth,
+ combinedAspect,
+ combinedPadding,
+ curRow,
+ curRowHeight,
+ wantedWidth,
+ preferredHeight,
+ newWidth,
+ padding,
+ $outerDiv,
+ $innerDiv,
+ $imageDiv,
+ $imageElm,
+ imageElm,
+ $caption,
+ i,
+ j,
+ avgZoom,
+ totalZoom = 0;
+
+ for ( i = 0; i < rows.length; i++ ) {
+ maxWidth = $gallery.width();
+ combinedAspect = 0;
+ combinedPadding = 0;
+ curRow = rows[ i ];
+ curRowHeight = 0;
+
+ for ( j = 0; j < curRow.length; j++ ) {
+ if ( curRowHeight === 0 ) {
+ if ( isFinite( curRow[ j ].height ) ) {
+ // Get the height of this row, by taking the first
+ // non-out of bounds height
+ curRowHeight = curRow[ j ].height;
+ }
+ }
+
+ if ( curRow[ j ].aspect === 0 || !isFinite( curRow[ j ].aspect ) ) {
+ // One of the dimensions are 0. Probably should
+ // not try to resize.
+ combinedPadding += curRow[ j ].width;
+ } else {
+ combinedAspect += curRow[ j ].aspect;
+ combinedPadding += curRow[ j ].width - curRow[ j ].imgWidth;
+ }
+ }
+
+ // Add some padding for inter-element spacing.
+ combinedPadding += 5 * curRow.length;
+ wantedWidth = maxWidth - combinedPadding;
+ preferredHeight = wantedWidth / combinedAspect;
+
+ if ( preferredHeight > curRowHeight * 1.5 ) {
+ // Only expand at most 1.5 times current size
+ // As that's as high a resolution as we have.
+ // Also on the off chance there is a bug in this
+ // code, would prevent accidentally expanding to
+ // be 10 billion pixels wide.
+ if ( i === rows.length - 1 ) {
+ // If its the last row, and we can't fit it,
+ // don't make the entire row huge.
+ avgZoom = ( totalZoom / ( rows.length - 1 ) ) * curRowHeight;
+ if ( isFinite( avgZoom ) && avgZoom >= 1 && avgZoom <= 1.5 ) {
+ preferredHeight = avgZoom;
+ } else {
+ // Probably a single row gallery
+ preferredHeight = curRowHeight;
+ }
+ } else {
+ preferredHeight = 1.5 * curRowHeight;
+ }
+ }
+ if ( !isFinite( preferredHeight ) ) {
+ // This *definitely* should not happen.
+ // Skip this row.
+ continue;
+ }
+ if ( preferredHeight < 5 ) {
+ // Well something clearly went wrong...
+ // Skip this row.
+ continue;
+ }
+
+ if ( preferredHeight / curRowHeight > 1 ) {
+ totalZoom += preferredHeight / curRowHeight;
+ } else {
+ // If we shrink, still consider that a zoom of 1
+ totalZoom += 1;
+ }
+
+ for ( j = 0; j < curRow.length; j++ ) {
+ newWidth = preferredHeight * curRow[ j ].aspect;
+ padding = curRow[ j ].width - curRow[ j ].imgWidth;
+ $outerDiv = curRow[ j ].$elm;
+ $innerDiv = $outerDiv.children( 'div' ).first();
+ $imageDiv = $innerDiv.children( 'div.thumb' );
+ $imageElm = $imageDiv.find( 'img' ).first();
+ imageElm = $imageElm.length ? $imageElm[ 0 ] : null;
+ $caption = $outerDiv.find( 'div.gallerytextwrapper' );
+
+ // Since we are going to re-adjust the height, the vertical
+ // centering margins need to be reset.
+ $imageDiv.children( 'div' ).css( 'margin', '0px auto' );
+
+ if ( newWidth < 60 || !isFinite( newWidth ) ) {
+ // Making something skinnier than this will mess up captions,
+ if ( newWidth < 1 || !isFinite( newWidth ) ) {
+ $innerDiv.height( preferredHeight );
+ // Don't even try and touch the image size if it could mean
+ // making it disappear.
+ continue;
+ }
+ } else {
+ $outerDiv.width( newWidth + padding );
+ $innerDiv.width( newWidth + padding );
+ $imageDiv.width( newWidth );
+ $caption.width( curRow[ j ].captionWidth + ( newWidth - curRow[ j ].imgWidth ) );
+ }
+
+ if ( imageElm ) {
+ // We don't always have an img, e.g. in the case of an invalid file.
+ imageElm.width = newWidth;
+ imageElm.height = preferredHeight;
+ } else {
+ // Not a file box.
+ $imageDiv.height( preferredHeight );
+ }
+ }
+ }
+ }() );
+ }
+
+ function handleResizeStart() {
+ $galleries.children( 'li' ).each( function () {
+ var imgWidth = $( this ).data( 'imgWidth' ),
+ imgHeight = $( this ).data( 'imgHeight' ),
+ width = $( this ).data( 'width' ),
+ captionWidth = $( this ).data( 'captionWidth' ),
+ $innerDiv = $( this ).children( 'div' ).first(),
+ $imageDiv = $innerDiv.children( 'div.thumb' ),
+ $imageElm, imageElm;
+
+ // Restore original sizes so we can arrange the elements as on freshly loaded page
+ $( this ).width( width );
+ $innerDiv.width( width );
+ $imageDiv.width( imgWidth );
+ $( this ).find( 'div.gallerytextwrapper' ).width( captionWidth );
+
+ $imageElm = $( this ).find( 'img' ).first();
+ imageElm = $imageElm.length ? $imageElm[ 0 ] : null;
+ if ( imageElm ) {
+ imageElm.width = imgWidth;
+ imageElm.height = imgHeight;
+ } else {
+ $imageDiv.height( imgHeight );
+ }
+ } );
+ }
+
+ function handleResizeEnd() {
+ $galleries.each( justify );
+ }
+
+ mw.hook( 'wikipage.content' ).add( function ( $content ) {
+ if ( isTouchScreen ) {
+ // Always show the caption for a touch screen.
+ $content.find( 'ul.mw-gallery-packed-hover' )
+ .addClass( 'mw-gallery-packed-overlay' )
+ .removeClass( 'mw-gallery-packed-hover' );
+ } else {
+ // Note use of just "a", not a.image, since we want this to trigger if a link in
+ // the caption receives focus
+ $content.find( 'ul.mw-gallery-packed-hover li.gallerybox' ).on( 'focus blur', 'a', function ( e ) {
+ // Confusingly jQuery leaves e.type as focusout for delegated blur events
+ var gettingFocus = e.type !== 'blur' && e.type !== 'focusout';
+ $( this ).closest( 'li.gallerybox' ).toggleClass( 'mw-gallery-focused', gettingFocus );
+ } );
+ }
+
+ $galleries = $content.find( 'ul.mw-gallery-packed-overlay, ul.mw-gallery-packed-hover, ul.mw-gallery-packed' );
+ // Call the justification asynchronous because live preview fires the hook with detached $content.
+ setTimeout( function () {
+ $galleries.each( justify );
+
+ // Bind here instead of in the top scope as the callbacks use $galleries.
+ if ( !bound ) {
+ bound = true;
+ $( window )
+ .resize( $.debounce( 300, true, handleResizeStart ) )
+ .resize( $.debounce( 300, handleResizeEnd ) );
+ }
+ } );
+ } );
+}( mediaWiki, jQuery ) );
--- /dev/null
+/*!
+ * Implement AJAX navigation for multi-page images so the user may browse without a full page reload.
+ */
+( function ( mw, $ ) {
+ /*jshint latedef:false */
+ var jqXhr, $multipageimage, $spinner,
+ cache = {},
+ cacheOrder = [];
+
+ /* Fetch the next page, caching up to 10 last-loaded pages.
+ * @param {string} url
+ * @return {jQuery.Promise}
+ */
+ function fetchPageData( url ) {
+ if ( jqXhr && jqXhr.abort ) {
+ // Prevent race conditions and piling up pending requests
+ jqXhr.abort();
+ }
+ jqXhr = undefined;
+
+ // Try the cache
+ if ( cache[ url ] ) {
+ // Update access freshness
+ cacheOrder.splice( $.inArray( url, cacheOrder ), 1 );
+ cacheOrder.push( url );
+ return $.Deferred().resolve( cache[ url ] ).promise();
+ }
+
+ // @todo Don't fetch the entire page. Ideally we'd only fetch the content portion or the data
+ // (thumbnail urls) and update the interface manually.
+ jqXhr = $.ajax( url ).then( function ( data ) {
+ return $( data ).find( 'table.multipageimage' ).contents();
+ } );
+
+ // Handle cache updates
+ jqXhr.done( function ( $contents ) {
+ jqXhr = undefined;
+
+ // Cache the newly loaded page
+ cache[ url ] = $contents;
+ cacheOrder.push( url );
+
+ // Remove the oldest entry if we're over the limit
+ if ( cacheOrder.length > 10 ) {
+ delete cache[ cacheOrder[ 0 ] ];
+ cacheOrder = cacheOrder.slice( 1 );
+ }
+ } );
+
+ return jqXhr.promise();
+ }
+
+ /* Fetch the next page and use jQuery to swap the table.multipageimage contents.
+ * @param {string} url
+ * @param {boolean} [hist=false] Whether this is a load triggered by history navigation (if
+ * true, this function won't push a new history state, for the browser did so already).
+ */
+ function switchPage( url, hist ) {
+ var $tr, promise;
+
+ // Start fetching data (might be cached)
+ promise = fetchPageData( url );
+
+ // Add a new spinner if one doesn't already exist and the data is not already ready
+ if ( !$spinner && promise.state() !== 'resolved' ) {
+ $tr = $multipageimage.find( 'tr' );
+ $spinner = $.createSpinner( {
+ size: 'large',
+ type: 'block'
+ } )
+ // Copy the old content dimensions equal so that the current scroll position is not
+ // lost between emptying the table is and receiving the new contents.
+ .css( {
+ height: $tr.outerHeight(),
+ width: $tr.outerWidth()
+ } );
+
+ $multipageimage.empty().append( $spinner );
+ }
+
+ promise.done( function ( $contents ) {
+ $spinner = undefined;
+
+ // Replace table contents
+ $multipageimage.empty().append( $contents.clone() );
+
+ bindPageNavigation( $multipageimage );
+
+ // Fire hook because the page's content has changed
+ mw.hook( 'wikipage.content' ).fire( $multipageimage );
+
+ // Update browser history and address bar. But not if we came here from a history
+ // event, in which case the url is already updated by the browser.
+ if ( history.pushState && !hist ) {
+ history.pushState( { tag: 'mw-pagination' }, document.title, url );
+ }
+ } );
+ }
+
+ function bindPageNavigation( $container ) {
+ $container.find( '.multipageimagenavbox' ).one( 'click', 'a', function ( e ) {
+ var page, uri;
+
+ // Generate the same URL on client side as the one generated in ImagePage::openShowImage.
+ // We avoid using the URL in the link directly since it could have been manipulated (bug 66608)
+ page = Number( mw.util.getParamValue( 'page', this.href ) );
+ uri = new mw.Uri( mw.util.wikiScript() )
+ .extend( { title: mw.config.get( 'wgPageName' ), page: page } )
+ .toString();
+
+ switchPage( uri );
+ e.preventDefault();
+ } );
+
+ $container.find( 'form[name="pageselector"]' ).one( 'change submit', function ( e ) {
+ switchPage( this.action + '?' + $( this ).serialize() );
+ e.preventDefault();
+ } );
+ }
+
+ $( function () {
+ if ( mw.config.get( 'wgNamespaceNumber' ) !== 6 ) {
+ return;
+ }
+ $multipageimage = $( 'table.multipageimage' );
+ if ( !$multipageimage.length ) {
+ return;
+ }
+
+ bindPageNavigation( $multipageimage );
+
+ // Update the url using the History API (if available)
+ if ( history.pushState && history.replaceState ) {
+ history.replaceState( { tag: 'mw-pagination' }, '' );
+ $( window ).on( 'popstate', function ( e ) {
+ var state = e.originalEvent.state;
+ if ( state && state.tag === 'mw-pagination' ) {
+ switchPage( location.href, true );
+ }
+ } );
+ }
+ } );
+}( mediaWiki, jQuery ) );
--- /dev/null
+/*!
+ * Animate patrol links to use asynchronous API requests to
+ * patrol pages, rather than navigating to a different URI.
+ *
+ * @since 1.21
+ * @author Marius Hoch <hoo@online.de>
+ */
+( function ( mw, $ ) {
+ if ( !mw.user.tokens.exists( 'patrolToken' ) ) {
+ // Current user has no patrol right, or an old cached version of user.tokens
+ // that didn't have patrolToken yet.
+ return;
+ }
+ $( function () {
+ var $patrolLinks = $( '.patrollink a' );
+ $patrolLinks.on( 'click', function ( e ) {
+ var $spinner, href, rcid, apiRequest;
+
+ // Start preloading the notification module (normally loaded by mw.notify())
+ mw.loader.load( 'mediawiki.notification' );
+
+ // Hide the link and create a spinner to show it inside the brackets.
+ $spinner = $.createSpinner( {
+ size: 'small',
+ type: 'inline'
+ } );
+ $( this ).hide().after( $spinner );
+
+ href = $( this ).attr( 'href' );
+ rcid = mw.util.getParamValue( 'rcid', href );
+ apiRequest = new mw.Api();
+
+ apiRequest.postWithToken( 'patrol', {
+ action: 'patrol',
+ rcid: rcid
+ } )
+ .done( function ( data ) {
+ // Remove all patrollinks from the page (including any spinners inside).
+ $patrolLinks.closest( '.patrollink' ).remove();
+ if ( data.patrol !== undefined ) {
+ // Success
+ var title = new mw.Title( data.patrol.title );
+ mw.notify( mw.msg( 'markedaspatrollednotify', title.toText() ) );
+ } else {
+ // This should never happen as errors should trigger fail
+ mw.notify( mw.msg( 'markedaspatrollederrornotify' ), { type: 'error' } );
+ }
+ } )
+ .fail( function ( error ) {
+ $spinner.remove();
+ // Restore the patrol link. This allows the user to try again
+ // (or open it in a new window, bypassing this ajax module).
+ $patrolLinks.show();
+ if ( error === 'noautopatrol' ) {
+ // Can't patrol own
+ mw.notify( mw.msg( 'markedaspatrollederror-noautopatrol' ), { type: 'warn' } );
+ } else {
+ mw.notify( mw.msg( 'markedaspatrollederrornotify' ), { type: 'error' } );
+ }
+ } );
+
+ e.preventDefault();
+ } );
+ } );
+}( mediaWiki, jQuery ) );
--- /dev/null
+( function ( mw, $ ) {
+ var supportsPlaceholder = 'placeholder' in document.createElement( 'input' );
+
+ // Break out of framesets
+ if ( mw.config.get( 'wgBreakFrames' ) ) {
+ // Note: In IE < 9 strict comparison to window is non-standard (the standard didn't exist yet)
+ // it works only comparing to window.self or window.window (http://stackoverflow.com/q/4850978/319266)
+ if ( window.top !== window.self ) {
+ // Un-trap us from framesets
+ window.top.location.href = location.href;
+ }
+ }
+
+ mw.hook( 'wikipage.content' ).add( function ( $content ) {
+ var $sortableTables;
+
+ // Run jquery.placeholder polyfill if placeholder is not supported
+ if ( !supportsPlaceholder ) {
+ $content.find( 'input[placeholder]' ).placeholder();
+ }
+
+ // Run jquery.makeCollapsible
+ $content.find( '.mw-collapsible' ).makeCollapsible();
+
+ // Lazy load jquery.tablesorter
+ $sortableTables = $content.find( 'table.sortable' );
+ if ( $sortableTables.length ) {
+ mw.loader.using( 'jquery.tablesorter', function () {
+ $sortableTables.tablesorter();
+ } );
+ }
+
+ // Run jquery.checkboxShiftClick
+ $content.find( 'input[type="checkbox"]:not(.noshiftselect)' ).checkboxShiftClick();
+ } );
+
+ // Things outside the wikipage content
+ $( function () {
+ var $nodes;
+
+ if ( !supportsPlaceholder ) {
+ // Exclude content to avoid hitting it twice for the (first) wikipage content
+ $( 'input[placeholder]' ).not( '#mw-content-text input' ).placeholder();
+ }
+
+ // Add accesskey hints to the tooltips
+ if ( document.querySelectorAll ) {
+ // If we're running on a browser where we can do this efficiently,
+ // just find all elements that have accesskeys. We can't use jQuery's
+ // polyfill for the selector since looping over all elements on page
+ // load might be too slow.
+ $nodes = $( document.querySelectorAll( '[accesskey]' ) );
+ } else {
+ // Otherwise go through some elements likely to have accesskeys rather
+ // than looping over all of them. Unfortunately this will not fully
+ // work for custom skins with different HTML structures. Input, label
+ // and button should be rare enough that no optimizations are needed.
+ $nodes = $( '#column-one a, #mw-head a, #mw-panel a, #p-logo a, input, label, button' );
+ }
+ $nodes.updateTooltipAccessKeys();
+
+ // Infuse OOUI widgets, if any are present
+ $nodes = $( '[data-ooui]' );
+ if ( $nodes.length ) {
+ mw.loader.using( 'mediawiki.widgets' ).done( function () {
+ $nodes.each( function () {
+ OO.ui.infuse( this );
+ } );
+ } );
+ }
+
+ } );
+
+}( mediaWiki, jQuery ) );
--- /dev/null
+( function ( mw, $ ) {
+
+ // Support: MediaWiki < 1.26
+ // Cached HTML will not yet have this from OutputPage::getHeadScripts.
+ document.documentElement.className = document.documentElement.className
+ .replace( /(^|\s)client-nojs(\s|$)/, '$1client-js$2' );
+
+ mw.page = {};
+
+ $( function () {
+ mw.util.init();
+
+ /**
+ * Fired when wiki content is being added to the DOM
+ *
+ * It is encouraged to fire it before the main DOM is changed (when $content
+ * is still detatched). However, this order is not defined either way, so you
+ * should only rely on $content itself.
+ *
+ * This includes the ready event on a page load (including post-edit loads)
+ * and when content has been previewed with LivePreview.
+ *
+ * @event wikipage_content
+ * @member mw.hook
+ * @param {jQuery} $content The most appropriate element containing the content,
+ * such as #mw-content-text (regular content root) or #wikiPreview (live preview
+ * root)
+ */
+ mw.hook( 'wikipage.content' ).fire( $( '#mw-content-text' ) );
+ } );
+
+}( mediaWiki, jQuery ) );
--- /dev/null
+/**
+ * Animate watch/unwatch links to use asynchronous API requests to
+ * watch pages, rather than navigating to a different URI.
+ *
+ * @class mw.page.watch.ajax
+ */
+( function ( mw, $ ) {
+ // The name of the page to watch or unwatch
+ var title = mw.config.get( 'wgRelevantPageName' );
+
+ /**
+ * Update the link text, link href attribute and (if applicable)
+ * "loading" class.
+ *
+ * @param {jQuery} $link Anchor tag of (un)watch link
+ * @param {string} action One of 'watch', 'unwatch'
+ * @param {string} [state="idle"] 'idle' or 'loading'. Default is 'idle'
+ */
+ function updateWatchLink( $link, action, state ) {
+ var msgKey, $li, otherAction;
+
+ // A valid but empty jQuery object shouldn't throw a TypeError
+ if ( !$link.length ) {
+ return;
+ }
+
+ // Invalid actions shouldn't silently turn the page in an unrecoverable state
+ if ( action !== 'watch' && action !== 'unwatch' ) {
+ throw new Error( 'Invalid action' );
+ }
+
+ // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
+ msgKey = state === 'loading' ? action + 'ing' : action;
+ otherAction = action === 'watch' ? 'unwatch' : 'watch';
+ $li = $link.closest( 'li' );
+
+ // Trigger a 'watchpage' event for this List item.
+ // Announce the otherAction value as the first param.
+ // Used to monitor the state of watch link.
+ // TODO: Revise when system wide hooks are implemented
+ if ( state === undefined ) {
+ $li.trigger( 'watchpage.mw', otherAction );
+ }
+
+ $link
+ .text( mw.msg( msgKey ) )
+ .attr( 'title', mw.msg( 'tooltip-ca-' + action ) )
+ .updateTooltipAccessKeys()
+ .attr( 'href', mw.util.wikiScript() + '?' + $.param( {
+ title: title,
+ action: action
+ } )
+ );
+
+ // Most common ID style
+ if ( $li.prop( 'id' ) === 'ca-' + otherAction ) {
+ $li.prop( 'id', 'ca-' + action );
+ }
+
+ if ( state === 'loading' ) {
+ $link.addClass( 'loading' );
+ } else {
+ $link.removeClass( 'loading' );
+ }
+ }
+
+ /**
+ * TODO: This should be moved somewhere more accessible.
+ *
+ * @private
+ * @param {string} url
+ * @return {string} The extracted action, defaults to 'view'
+ */
+ function mwUriGetAction( url ) {
+ var action, actionPaths, key, i, m, parts;
+
+ // TODO: Does MediaWiki give action path or query param
+ // precedence? If the former, move this to the bottom
+ action = mw.util.getParamValue( 'action', url );
+ if ( action !== null ) {
+ return action;
+ }
+
+ actionPaths = mw.config.get( 'wgActionPaths' );
+ for ( key in actionPaths ) {
+ if ( actionPaths.hasOwnProperty( key ) ) {
+ parts = actionPaths[ key ].split( '$1' );
+ for ( i = 0; i < parts.length; i++ ) {
+ parts[ i ] = mw.RegExp.escape( parts[ i ] );
+ }
+ m = new RegExp( parts.join( '(.+)' ) ).exec( url );
+ if ( m && m[ 1 ] ) {
+ return key;
+ }
+
+ }
+ }
+
+ return 'view';
+ }
+
+ // Expose public methods
+ mw.page.watch = {
+ updateWatchLink: updateWatchLink
+ };
+
+ $( function () {
+ var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
+ '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
+ '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' );
+
+ // Allowing people to add inline animated links is a little scary
+ $links = $links.filter( ':not( #bodyContent *, #content * )' );
+
+ $links.click( function ( e ) {
+ var action, api, $link;
+
+ // Start preloading the notification module (normally loaded by mw.notify())
+ mw.loader.load( 'mediawiki.notification' );
+
+ action = mwUriGetAction( this.href );
+
+ if ( action !== 'watch' && action !== 'unwatch' ) {
+ // Could not extract target action from link url,
+ // let native browsing handle it further
+ return true;
+ }
+ e.preventDefault();
+ e.stopPropagation();
+
+ $link = $( this );
+
+ if ( $link.hasClass( 'loading' ) ) {
+ return;
+ }
+
+ updateWatchLink( $link, action, 'loading' );
+
+ api = new mw.Api();
+
+ api[ action ]( title )
+ .done( function ( watchResponse ) {
+ var otherAction = action === 'watch' ? 'unwatch' : 'watch';
+
+ mw.notify( $.parseHTML( watchResponse.message ), {
+ tag: 'watch-self'
+ } );
+
+ // Set link to opposite
+ updateWatchLink( $link, otherAction );
+
+ // Update the "Watch this page" checkbox on action=edit when the
+ // page is watched or unwatched via the tab (bug 12395).
+ $( '#wpWatchthis' ).prop( 'checked', watchResponse.watched !== undefined );
+ } )
+ .fail( function () {
+ var cleanTitle, msg, link;
+
+ // Reset link to non-loading mode
+ updateWatchLink( $link, action );
+
+ // Format error message
+ cleanTitle = title.replace( /_/g, ' ' );
+ link = mw.html.element(
+ 'a', {
+ href: mw.util.getUrl( title ),
+ title: cleanTitle
+ }, cleanTitle
+ );
+ msg = mw.message( 'watcherrortext', link );
+
+ // Report to user about the error
+ mw.notify( msg, {
+ tag: 'watch-self',
+ type: 'error'
+ } );
+ } );
+ } );
+ } );
+
+}( mediaWiki, jQuery ) );